package cn.seecoder.ai.serviceImpl;


import cn.seecoder.ai.dao.ConfigRepository;
import cn.seecoder.ai.dao.ModelRepository;
import cn.seecoder.ai.exception.AIExternalException;
import cn.seecoder.ai.model.po.Config;
import cn.seecoder.ai.model.vo.ConfigVO;
import cn.seecoder.ai.model.vo.FileFieldVO;
import cn.seecoder.ai.model.vo.FileInfoVO;
import cn.seecoder.ai.service.ConfigService;
import cn.seecoder.ai.service.FileInfoService;
import cn.seecoder.ai.utils.POJOConverter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author fanyanpeng
 * @date 2023/4/14 22:20
 */
@Slf4j
@Service
public class ConfigServiceImpl implements ConfigService {


    @Autowired
    FileInfoService fileInfoService;

    @Autowired
    ConfigRepository configRepository;


    // fyp: 本处跨层依赖了其他模块
    @Autowired
    ModelRepository modelRepository;

//    一个关于业务逻辑层依赖关系的思考：
//    在spring后端编程中，分为三个层进行开发，在service层中，有ConfigService，和ProductService，
//    其中ProductService依赖于ConfigService，在数据库中product持有config的外键，
//    当在configService中删除一个config对象时，要求数据库中没有product持有他的外键，
//    才能成功删除，此时ConfigService应该依赖ProductDAO吗？
//    目前采取的方案是直接依赖了底层的DAO来实现删除时的检测（若使用config的model数目大于1，就不能删除config）

    /**
     * 新建配置信息
     *
     * @param configVO
     * @return cn.seecoder.ai.model.vo.ConfigVO
     * @author fanyanpeng
     * @date 2023/4/14 22:17
     */
    @Override
    public ConfigVO createConfig(ConfigVO configVO) {

        Integer testFileInfo = configVO.getTestFileInfoId();
        Integer fileInfoId = configVO.getFileInfoId();
        if(testFileInfo!=null){
            dataTypeMatches(fileInfoId,testFileInfo);
        }
        //转换为po对象
        Config config = toPO(configVO);

        //配置创建时间
        config.setCreatedTime(new Date());
        // 存入数据库
        config = configRepository.save(config);
        return toVO(config);
    }



    // 判断两个文件字段列表是否匹配
    private void dataTypeMatches(Integer trainFileInfoId, Integer testFileInfoId){
        FileInfoVO trainFileInfoVO = fileInfoService.getFileInfo(0,trainFileInfoId);
        FileInfoVO testFileInfoVO = fileInfoService.getFileInfo(0,testFileInfoId);

        // 文件列表可能不是按照字段名排序的，所以先排序
        List<FileFieldVO> trainFileFieldVOList = getFileFieldVOsSorted(trainFileInfoVO);
        List<FileFieldVO> testFileFieldVOList = getFileFieldVOsSorted(testFileInfoVO);
        // 若个数不等，抛出异常
        if(trainFileFieldVOList.size()!=testFileFieldVOList.size()){
            throw AIExternalException.trainSetAndTestSetDataTypeMismatched();
        }
        // 若个数相等，但是类型不匹配，抛出异常
        for(int i=0;i<trainFileFieldVOList.size();i++){
            if(!trainFileFieldVOList.get(i).getFieldName().equals(testFileFieldVOList.get(i).getFieldName())
                    || !trainFileFieldVOList.get(i).getDataType().equals(testFileFieldVOList.get(i).getDataType())){
                throw AIExternalException.trainSetAndTestSetDataTypeMismatched();
            }
        }
    }
    private List<FileFieldVO> getFileFieldVOsSorted(FileInfoVO fileInfoVO){
        List<FileFieldVO> fileFieldVOs = fileInfoVO.getFileFields();
        fileFieldVOs.sort(Comparator.comparing(FileFieldVO::getFieldName));

        return fileFieldVOs;
    }

    /**
     * 获取用户配置列表
     *
     * @param userId
     * @return java.util.List<cn.seecoder.ai.model.vo.ConfigVO>
     * @author fanyanpeng
     * @date 2023/4/14 22:18
     */
    @Override
    public List<ConfigVO> getConfigList(Integer userId) {
        List<Config> configList = configRepository.findByUserIdOrderByCreatedTimeDesc(userId);
        //将所有po转为vo，收集为一个列表
        List<ConfigVO> configVOList = configList.stream()
                .map(config->toVO(config))
                .collect(Collectors.toList());

        return configVOList;
    }

    /**
     * 查看单个配置信息
     *
     * @param userId
     * @param configId
     * @return
     * @author fanyanpeng
     * @date 2023/4/14 22:18
     */
    @Override
    public ConfigVO getConfig(Integer userId, Integer configId) {
        Optional<Config> configOptional = configRepository.findById(configId);
        // 若没有找到，抛出异常给全局异常处理
        if(!configOptional.isPresent()){
            throw AIExternalException.configNotFoundByConfigId();
        }
        Config config = configOptional.get();
        ConfigVO configVO = toVO(config);

        return configVO;
    }

    /**
     * 删除一个配置
     *
     * @param userId
     * @param configId
     * @return cn.seecoder.ai.model.vo.ConfigVO
     * @author fanyanpeng
     * @date 2023/4/14 22:19
     */
    @Override
    public Boolean deleteConfig(Integer userId, Integer configId) {
        int modelRelyOnThisConfigCount = modelRepository.countByConfigId(configId);
        if(modelRelyOnThisConfigCount>0){
            throw AIExternalException.modelRelyOnThisConfig();
        }
        configRepository.deleteById(configId);
        return true;
    }




    /**
     * 转换为po对象，目前仅需要直接复制属性，
     * 若后续需要添加额外属性，还需要进一步配置转换。
     * @author   fanyanpeng
     * @date 2023/4/14 22:22
     * @param configVO
     * @return cn.seecoder.ai.model.po.Config
     */
    private Config toPO(ConfigVO configVO){
        return (Config) POJOConverter.convert(configVO);
    }

    /**
     * 转换为vo对象，目前仅需要直接复制属性，
     * 若后续需要添加额外属性，还需要进一步配置转换。
     * @author   fanyanpeng
     * @date 2023/4/14 22:22
     * @param config
     * @return cn.seecoder.ai.model.po.Config
     */
    private ConfigVO toVO(Config config){
        ConfigVO configVO = (ConfigVO) POJOConverter.convert(config);
        addAdditionalName(configVO);
        return  configVO;
    }

    private void addAdditionalName(ConfigVO configVO){
        try {
            FileInfoVO fileInfoVO = fileInfoService.getFileInfo(0, configVO.getFileInfoId());
            configVO.setFileInfoName(fileInfoVO.getFileName());
        }catch (Exception e){
            log.warn(e.getMessage());
        }

    }


}
